package org.platform.processor.diagnose;

import cn.hutool.core.convert.Convert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.platform.constant.DeviceTypeConstant;
import org.platform.constant.DiagnoseConst;
import org.platform.dao.mapper.*;
import org.platform.mo.bussmo.common.FileHeader;
import org.platform.mo.bussmo.diagnose.test.*;
import org.platform.mo.csmo.cmd21002.TestDiagnoseTaskStopRequestParam;
import org.platform.mo.csmo.common.LoopTests;
import org.platform.mo.csmo.common.TestChannelBean;
import org.platform.mo.db.*;
import org.platform.processor.IBaseProcessor;
import org.platform.processor.knowledge.TestProcesor;
import org.platform.tool.helper.FileHelper;
import org.platform.tool.helper.ResourceHelper;
import org.platform.tool.util.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static java.lang.Thread.sleep;

/**
 * 测试诊断
 *
 * @author FengJie
 * @date 2018/5/10
 */
@Service
@Slf4j
public class DiagnoseTaskProcessor implements IBaseProcessor {
    /**
     * 测试取消标识
     */
    public static final String TEST_CANCEL = "TESTCANCEL";
    /**
     * 标识符 测试申请
     */
    private static final String TEST_PLAN = "TESTPLAN";
    /**
     * 发送方 FDS
     */
    private static final String SOURCE = "FDS";
    /**
     * 接收方 TASK
     */
    private static final String DESTINATION = "TASK";
    /**
     * 文件类型
     */
    private static final String FILE_TYPE = ".xml";

    @Autowired
    private FdDiagnosetaskMapper diagnoseTaskMapper;
    @Autowired
    private FdDiagnosetaskTestMapper testMapper;
    @Autowired
    private FdTestchannelMapper channelMapper;
    @Autowired
    private FdTestparamsLinkMapper linkMapper;
    @Autowired
    private FdTestparamsGtMapper gtMapper;
    @Autowired
    private FdTestparamsBerMapper berMapper;
    @Autowired
    private FdTestchannelMapper testChannelMapper;
    @Autowired
    private TestProcesor testProcesor;
    @Autowired
    private ResourceHelper resourceHelper;
    @Autowired
    private FileHelper fileHelper;

    /**
     * 发起测试申请
     *
     * @param task 诊断任务
     */
    public void testPlan(FdDiagnosetask task) throws IOException {
        // 文件头部分
        FileHeader header = new FileHeader();
        header.setIdentificationCode(TEST_PLAN);
        header.setSource(SOURCE);
        header.setDestination(DESTINATION);
        header.setCreatedTime(TimeUtils.getSystemCurrentTime());

        //内容部分
        TestPlan testPlan = getTestPlanParam(task.getDiagnoseid());
        testPlan.setStationID(task.getSto());
        testPlan.setTestPlanID(task.getDiagnoseid());
        testPlan.setTestType(getChineseType(task.getType()));
        testPlan.setUserID(task.getCreateusername());

        //xml整体
        TestDiagnoseXml testDiagnoseXml = new TestDiagnoseXml();
        testDiagnoseXml.setFileHeader(header);
        testDiagnoseXml.setContent(testPlan);

        String sXml = XmlUtils.beanToXml(testDiagnoseXml);
        String replaceXml = StringUtils.isEmpty(sXml) ? "" : sXml.replaceAll("&quot;", "\"");
        writeFileAndUpload(replaceXml);
    }

    /**
     * 终止测试
     *
     * @param requestParam
     * @return
     */
    public void testCancel(TestDiagnoseTaskStopRequestParam requestParam) throws IOException {
        //更新本地数据库状态
        FdDiagnosetask task = new FdDiagnosetask();
        task.setDiagnoseid(requestParam.getTestPlanID());
        task.setStatus(DiagnoseConst.DIAGNOSE_STATUS_ABORT);
        diagnoseTaskMapper.updateByPrimaryKeySelective(task);
        FdDiagnosetaskTest testTask = new FdDiagnosetaskTest();
        testTask.setStatus(DiagnoseConst.DIAGNOSE_STATUS_ABORT);
        Example example = new Example(FdDiagnosetaskTest.class);
        example.createCriteria().andEqualTo("status", DiagnoseConst.DIAGNOSE_STATUS_ABORT);
        testMapper.updateByExampleSelective(testTask, example);

        TestCancel testCancel = getTestCancelParam(requestParam);
        String xml = XmlUtils.beanToXml(testCancel);
        writeFileAndUpload(xml);
    }

    /**
     * 根据知识id创建测试诊断任务
     *
     * @param knowledgeID 知识id
     * @param deviceId    设备id
     * @return 诊断id
     */
    public String addTestDiagnoseByKnowledgeID(Long knowledgeID, String deviceId, String to, Integer type) {
        //初始化主表信息
        FdDiagnosetask diagnoseTask = new FdDiagnosetask();
        String diagnoseId = "FDS_TP" + SerialNumberUtils.getInstance().getSerialNumber();
        diagnoseTask.setDiagnoseid(diagnoseId);
        diagnoseTask.setTaskid(diagnoseId);
        diagnoseTask.setIsmain(0);
        diagnoseTask.setKnowledgeid(knowledgeID);
        diagnoseTask.setStatus(DiagnoseConst.DIAGNOSE_STATUS_WAIT);
        diagnoseTask.setStarttime(System.currentTimeMillis());
        diagnoseTask.setEndtime(0L);
        diagnoseTask.setSfrom(resourceHelper.getStationName());
        diagnoseTask.setSto(to);
        diagnoseTask.setDeviceid(deviceId);
        diagnoseTask.setType(type);

        diagnoseTaskMapper.insertSelective(diagnoseTask);
        //初始化诊断测试表
        FdDiagnosetaskTest test = new FdDiagnosetaskTest();
        BeanUtils.copyProperties(diagnoseTask, test);
        test.setId(diagnoseId);

        FdKbTestparams testParams = testProcesor.getTestParamsByKnowledgeID(knowledgeID, diagnoseTask.getSfrom());
        FdKbTesttemplate testTemplate = testProcesor.getTestTemplate(knowledgeID, diagnoseTask.getSfrom());
        //todo 这里需要重新设置超时时间,重配置文件中获取

        saveTestParam(test, diagnoseId, testParams.getTestparms(), testParams.getTesttype(), testTemplate.getLooptestid());

        return diagnoseId;
    }

    /**
     * 创建子诊断
     *
     * @param treeNode
     */
    public String addSubTestDiagnoseByTreeNode(FdDiagnoseTaskTreeNode treeNode) {
        //初始化主表信息
        FdDiagnosetask diagnoseTask = diagnoseTaskMapper.selectByPrimaryKey(treeNode.getDiagnoseid());
        String parentDiagnoseId = diagnoseTask.getDiagnoseid();
        String diagnoseId = "FDS_TP" + SerialNumberUtils.getInstance().getSerialNumber();
        diagnoseTask.setDiagnoseid(diagnoseId);
        diagnoseTask.setTaskid(diagnoseId);
        diagnoseTask.setIsmain(1);
        diagnoseTask.setDeviceid(treeNode.getDeviceid());
        diagnoseTask.setParentid(parentDiagnoseId);
        diagnoseTask.setStatus(DiagnoseConst.DIAGNOSE_STATUS_WAIT);
        diagnoseTask.setStarttime(System.currentTimeMillis());
        diagnoseTask.setEndtime(0L);
        diagnoseTaskMapper.insertSelective(diagnoseTask);

        //初始化诊断测试表
        FdDiagnosetaskTest test = new FdDiagnosetaskTest();
        BeanUtils.copyProperties(diagnoseTask, test);
        test.setId(diagnoseId);

        FdKbTestparams testParams = testProcesor.getTestParamsByKnowledgeID(treeNode.getKnowledgeid(), diagnoseTask.getSfrom());
        FdKbTesttemplate testTemplate = testProcesor.getTestTemplate(treeNode.getKnowledgeid(), diagnoseTask.getSfrom());
        //todo 这里需要重新设置超时时间,重配置文件中获取

        saveTestParam(test, diagnoseId, testParams.getTestparms(), testParams.getTesttype(), testTemplate.getLooptestid());

        return diagnoseId;
    }

    /**
     * 保存测试参数
     */
    public void saveTestParam(FdDiagnosetaskTest test, String diagnoseID, String json, Integer testType, String loopType) {
        int testSubID = 1;
        try {
            testMapper.insert(test);
            LoopTests loopTests = JsonUtils.jsonToBean(json, LoopTests.class, true);
            for (TestChannelBean channel : loopTests.getTestChannelBeanList()) {
                FdTestchannel testChannel = new FdTestchannel();
                testChannel.setDiagnoseid(diagnoseID);
                Integer channelId = channel.getNumber();
                testChannel.setChannelid(channelId);
                testChannel.setTesttype(testType);
                testChannelMapper.insert(testChannel);

                switch (testType) {
                    case DiagnoseConst.GT_DIAGNOSE_TEST:
                        FdTestparamsGt paramGt = new FdTestparamsGt();
                        paramGt.setId(ComUtils.getUUID());
                        paramGt.setDiagnoseid(diagnoseID);
                        paramGt.setChannelid(channelId);
                        paramGt.setTestsubid(formatTestSubID(testSubID));
                        initGtTestParam(channel.getDeviceParamList(), paramGt);
                        gtMapper.insert(paramGt);
                        break;
                    case DiagnoseConst.LINK_DIAGNOSE_TEST:
                        FdTestparamsLink paramLink = new FdTestparamsLink();
                        paramLink.setId(ComUtils.getUUID());
                        paramLink.setDiagnoseid(diagnoseID);
                        paramLink.setChannelid(channelId);
                        paramLink.setTestsubid(formatTestSubID(testSubID));
                        paramLink.setDevlink(getLink(channel));
                        paramLink.setLooptype(loopType);
                        initLinkTestParam(channel.getDeviceParamList(), paramLink);
                        linkMapper.insert(paramLink);
                        break;
                    case DiagnoseConst.BER_DIAGNOSE_TEST:
                        FdTestparamsBer paramBer = new FdTestparamsBer();
                        paramBer.setId(ComUtils.getUUID());
                        paramBer.setDiagnoseid(diagnoseID);
                        paramBer.setChannelid(channelId);
                        paramBer.setTestsubid(formatTestSubID(testSubID));
                        paramBer.setDevlink(getLink(channel));
                        initBerTestParam(channel.getDeviceParamList(), paramBer);
                        berMapper.insert(paramBer);
                        break;
                    case DiagnoseConst.ANT_DIAGNOSE_TEST:
                        //现在没有私服诊断了
                        break;
                    default:
                }
                testSubID++;
            }
        } catch (IOException e) {
            log.error("【测试诊断参数】解析失败,错误信息:", e);
            e.printStackTrace();
        }
    }

    /**
     * 获取子测试诊断的结果
     *
     * @param subDiagnoseTaskId 创建的子诊断ID
     * @param treeNode          当前诊断的父诊断节点
     * @return
     */
    public boolean getSubDiagnoseTaskResult(String subDiagnoseTaskId, FdDiagnoseTaskTreeNode treeNode) {

        //todo 这里的超时机制需要从 [配置文件中读取]

        int count = 60;
        for (int i = 0; i < count; i++) {
            FdDiagnosetask subDiagnoseTask = diagnoseTaskMapper.selectByPrimaryKey(subDiagnoseTaskId);
            switch (subDiagnoseTask.getStatus()) {
                //case DiagnoseConst.DIAGNOSE_STATUS_EXECUTE:
                case DiagnoseConst.DIAGNOSE_STATUS_SUCCESS:
                    return true;
                case DiagnoseConst.DIAGNOSE_STATUS_FAILURE:
                    return false;
                case DiagnoseConst.DIAGNOSE_STATUS_ABORT:
                    treeNode.setStatus(DiagnoseConst.DIAGNOSE_STATUS_ABORT);
                    return false;
                default:
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            }
        }
        treeNode.setStatus(DiagnoseConst.DIAGNOSE_STATUS_ABORT);
        return false;
    }

    /**
     * 获取链路配置
     *
     * @param channel
     * @return
     */
    private String getLink(TestChannelBean channel) {
        return getDeviceLink(channel.getDeviceLinkList());
    }

    /**
     * 解析设备链路
     *
     * @param deviceLinkList 设备链路
     * @return
     */
    public String getDeviceLink(List<DeviceLink> deviceLinkList) {
        StringBuilder builder = new StringBuilder();
        for (DeviceLink device : deviceLinkList) {
            String devType = device.getDevType();
            final String devId = device.getDevId();
            DeviceTypeConstant.LINK_TEST_DEV_MAP.forEach((k, v) -> {
                if ("LAN".equals(devType)) {
                    if (!v.contains(devType)) {
                        builder.append(k).append("=").append("\"").append("NA").append("\"").append(";");
                    } else {
                        builder.append(k).append("=").append("\"").append(devId).append("\"").append(";");
                    }
                } else if (v.contains(devType)) {
                    builder.append(k).append("=").append("\"").append(devId).append("\"").append(";");
                }
            });
        }
        String s = builder.toString();
        if (s.length() > 1) {
            s = s.substring(0, s.length() - 1);
        }
        return s;
    }

    /**
     * 解析设备参数
     * {DeviceID="XX",Parms {K1，V1;K2,V2}}
     */
    public String getDeviceParam(List<DeviceParam> deviceParamList) {
        StringBuilder builder = new StringBuilder();
        builder.append("{");
        deviceParamList.forEach(deviceParam -> {
            builder.append("DeviceID=\"").append(deviceParam.getDevId()).append("\",Parms{");

            deviceParam.getParam().forEach(devAttrValue ->
                    builder.append(devAttrValue.getAttrId()).append(",")
                            .append(devAttrValue.getAttrValue())
                            .append(";"));
            //删除最后一个 ;
            builder.deleteCharAt(builder.length() - 1);
            builder.append("}");

        });
        builder.append("}");
        return builder.toString();
    }

    /**
     * 初始化测试参数
     *
     * @param
     * @param testParam
     */
    private void initLinkTestParam(List<DeviceParam> deviceParamList, FdTestparamsLink testParam) {
        for (DeviceParam dev : deviceParamList) {
            for (DevAttrValue attr : dev.getParam()) {
                String name = attr.getAttrId();
                String value = attr.getAttrValue();

                switch (name) {
                    case "Frequency":
                        testParam.setFrequency(ComUtils.strToInteger(value));
                        break;
                    case "ModOutputLevel":
                        testParam.setModoutputlevel(ComUtils.strToFloat(value));
                        break;
                    case "ModOutputMode":
                        testParam.setModoutputmode(ComUtils.strToInteger(value));
                        break;
                    case "UCAtten":
                        testParam.setUcatten(ComUtils.strToInteger(value));
                        break;
                    case "TestOTAtten":
                        testParam.setTestotatten(ComUtils.strToInteger(value));
                        break;
                    case "DataOTAttenAtten":
                        testParam.setDataotattenatten(ComUtils.strToInteger(value));
                        break;
                    case "DCAtten":
                        testParam.setDcatten(ComUtils.strToInteger(value));
                        break;
                    default:
                }
            }
        }
    }

    /**
     * 初始化gt参数
     *
     * @param deviceParamList
     * @param paramGt
     */
    private void initGtTestParam(List<DeviceParam> deviceParamList, FdTestparamsGt paramGt) {
        deviceParamList.forEach(deviceParam -> deviceParam.getParam().forEach(dev -> {
            switch (dev.getAttrId()) {
                case "AntennaID":
                    paramGt.setAntennaid(dev.getAttrValue());
                    break;
                case "RadioSource":
                    paramGt.setRadiosource(dev.getAttrValue());
                    break;
                case "TestChl":
                    paramGt.setTestchl(dev.getAttrValue());
                    break;
                case "Polarization":
                    paramGt.setPolarization(dev.getAttrValue());
                    break;
                case "FreqList":
                    paramGt.setFrequency(dev.getAttrValue());
                    break;
                default:
            }
        }));
    }

    /**
     * GT 参数赋值
     *
     * @param deviceParamList
     * @param paramBer
     */
    private void initBerTestParam(List<DeviceParam> deviceParamList, FdTestparamsBer paramBer) {
        Field[] fields = paramBer.getClass().getDeclaredFields();
        deviceParamList.forEach(deviceParam -> deviceParam.getParam().forEach(dev -> {
            //通过反射,给相同的属性赋值
            for (Field field : fields) {
                String fieldName = field.getName();
                String fieldLowerCaseName = fieldName.toLowerCase();
                //删除所有的下划线(实体类中没有下划线)
                String attrId = dev.getAttrId().replaceAll("_", "");
                String lowerCaseAttrId = attrId.toLowerCase();
                //不区分大小写
                if (fieldLowerCaseName.equals(lowerCaseAttrId)) {
                    field.setAccessible(true);
                    Object value = Convert.convert(field.getType(), dev.getAttrValue());
                    try {
                        field.set(paramBer, value);
                    } catch (Exception e) {
                        log.error("GT参数{}赋值{}失败:,异常信息:{}", attrId, value, e);
                    }
                }
            }
        }));
    }

    /**
     * 格式化:测试子项序号
     *
     * @param testSubID
     * @return
     */
    private String formatTestSubID(int testSubID) {
        if (testSubID > SerialNumberUtils.MAX) {
            testSubID = SerialNumberUtils.MIN;
        }
        return String.format("%03d", testSubID);
    }

    /**
     * 获取测试申请参数
     */
    private TestPlan getTestPlanParam(String diagnoseID) {
        TestPlan requestBean = new TestPlan();
        Example baseExample = new Example(FdDiagnosetaskTest.class);
        baseExample.createCriteria().andEqualTo("diagnoseid", diagnoseID);
        FdDiagnosetaskTest fdDiagnosetaskTest = testMapper.selectByPrimaryKey(diagnoseID);
        //计算预计时间
        List<FdTestchannel> channelList = channelMapper.selectByExample(baseExample);
        Integer costTime = fdDiagnosetaskTest.getCosttime();
        Long duration = ComUtils.intToLong(costTime * channelList.size());
        requestBean.setDuration(duration);
        requestBean.setTestStartTime(TimeUtils.timestampsToStr(fdDiagnosetaskTest.getStarttime(), "yyyy-MM-dd HH:mm:ss"));

        switch (fdDiagnosetaskTest.getTesttype()) {
            case DiagnoseConst.GT_DIAGNOSE_TEST:
                List<FdTestparamsGt> gtList = gtMapper.selectByExample(baseExample);
                List<GTTestParam> gtTestParamList = new ArrayList<>(gtList.size());
                Map<Integer, List<FdTestparamsGt>> channelMap = gtList.stream().collect(Collectors.groupingBy(FdTestparamsGt::getChannelid));
                //遍历所有的通道
                channelMap.forEach((channel, gt) -> {
                    GTTestParam gtTestParam = new GTTestParam();
                    if (gt.size() != 0) {
                        BeanUtils.copyProperties(gt.get(0), gtTestParam);
                    }
                    //G/T值测试通道参数
                    gt.forEach(gt1 -> {
                        List<GTChlParam> gtChlParamList = new ArrayList<>(gtList.size());
                        GTChlParam gtChlParam = new GTChlParam();
                        BeanUtils.copyProperties(gt1, gtChlParam);
                        String frequency = gt1.getFrequency();
                        if (StringUtils.isNotEmpty(frequency)) {
                            String[] frequencys = frequency.split(",");

                            List<Integer> frequencyList = new ArrayList<>(frequencys.length);
                            Arrays.stream(frequencys).forEach(f -> frequencyList.add(ComUtils.strToInteger(f)));

                            FreqList freqList = new FreqList();
                            freqList.setFrequency(frequencyList);

                            gtChlParam.setFreqList(freqList);
                        }
                        gtChlParamList.add(gtChlParam);
                        gtTestParam.setGtChlParamList(gtChlParamList);
                    });
                    gtTestParamList.add(gtTestParam);
                });
                requestBean.setGtTestParamList(gtTestParamList);
                break;
            case (DiagnoseConst.LINK_DIAGNOSE_TEST):
                List<FdTestparamsLink> linkList = linkMapper.selectByExample(baseExample);
                List<LinkTestParam> linkParamList = BeansUtils.copyListProperties(linkList, LinkTestParam.class);
                requestBean.setLinkTestParamList(linkParamList);
                break;
            default:
                List<FdTestparamsBer> berList = berMapper.selectByExample(baseExample);
                List<BERTestParam> breParamList = BeansUtils.copyListProperties(berList, BERTestParam.class);
                requestBean.setBerTestParamList(breParamList);
        }
        return requestBean;
    }

    /**
     * 获取测试取消参数
     */
    private TestCancel getTestCancelParam(TestDiagnoseTaskStopRequestParam requestParam) {
        //body: 取消申请
        TestCancelContent requestBean = new TestCancelContent();
        BeanUtils.copyProperties(requestParam, requestBean);

        //head: 文件头
        FileHeader header = new FileHeader();
        header.setIdentificationCode(TEST_CANCEL);
        header.setSource(SOURCE);
        header.setDestination(DESTINATION);
        header.setCreatedTime(TimeUtils.getSystemCurrentTime());

        //xml文档
        TestCancel testCancel = new TestCancel();
        testCancel.setFileHeader(header);
        testCancel.setContent(requestBean);
        return testCancel;
    }

    private String getChineseType(int type) {
        String strType;
        switch (type) {
            case DiagnoseConst.ANT_DIAGNOSE_TEST:
                strType = "AntTest";
                break;
            case DiagnoseConst.GT_DIAGNOSE_TEST:
                strType = "GTTest";
                break;
            case DiagnoseConst.LINK_DIAGNOSE_TEST:
                strType = "LinkTest";
                break;
            case DiagnoseConst.BER_DIAGNOSE_TEST:
                strType = "BERTest";
                break;
            default:
                strType = "";
        }
        return strType;
    }

    /**
     * 把文件写入本地,同时上传到服务器上
     *
     * @param xml xml内容
     */
    private void writeFileAndUpload(String xml) throws IOException {
        String dir = resourceHelper.getLocalRootDir() + resourceHelper.getLocalArchivingDir();
        String time = TimeUtils.getTimeByFormat("yyyyMMddHHmmss");
        String serialnumber = SerialNumberUtils.getInstance().getSerialNumber();
        String fileName = TEST_PLAN + "_" + SOURCE + "_" + DESTINATION + "_" + time + "_" + serialnumber;
        String file = StringUtils.join(dir, fileName, FILE_TYPE);

        //先创建文件到本地备份目录
        fileHelper.writeFileContent(file, xml);
        //将文件发送到ftp上
        FtpClientUtil.getInstance(resourceHelper.getFtpIp(), resourceHelper.getPtpUsername(), resourceHelper.getPtpPassword())
                .upload(file, resourceHelper.getFtpSendDir());
    }
}
